package netbolt

import (
	"context"
	"fmt"
	"google.golang.org/grpc/metadata"
	"io"
	"sync"
	"time"
)

var (
	sessions             sync.Map
	keySoltSerializer, _ = NewSerializer(BIN)

	//notLoginResp = Response{Status: -1, Msg: "Not Login"}
)

type Session struct {
	token    string
	used     string
	created  int64
	username string
	pwd      string
}

type NetBoltService struct {
}

func checkToken(token string) bool {
	_, ok := sessions.Load(token)
	return ok
}

func getIncomingToken(ctx context.Context) (string, error) {
	md, ok := metadata.FromIncomingContext(ctx)

	if ok {
		info, ok := md["token"]
		if !ok {
			return "", TICKET_FAIL
		}
		if len(info) <= 0 {
			return "", TICKET_FAIL
		}

		return info[0], nil
	} else {
		return "", TICKET_FAIL
	}
}

func getIncomingTokenAndBbName(ctx context.Context) (string, string, error) {
	md, ok := metadata.FromIncomingContext(ctx)

	if ok {

		token := ""
		info, ok := md["token"]
		if ok && len(info) > 0 {
			token = info[0]
		}

		dbname := ""
		info, ok = md["dbname"]
		if ok && len(info) > 0 {
			dbname = info[0]
		}

		return token, dbname, nil
	} else {
		return "", "", TICKET_FAIL
	}
}

func findSessionByUsername(username string) *Session {

	var found *Session
	sessions.Range(func(key interface{}, val interface{}) bool {

		temp := val.(*Session)
		if temp.username == username {
			found = temp
			return false
		}
		return true
	})

	return found
}

func getSession(ticket string) (*Session, bool) {
	ret, ok := sessions.Load(ticket)
	if ok {
		return ret.(*Session), ok
	} else {
		return nil, ok
	}

}
func (s *NetBoltService) ListDBs(ctx context.Context, in *Ticket) (*DBNameList, error) {

	if _, ok := sessions.Load(in.Token); ok {
		dbLock.RLock()
		defer dbLock.RUnlock()
		namestrs := make([]string, 0)

		Databases.Range(func(key, value interface{}) bool {

			namestrs = append(namestrs, value.(*NetBoltDBInstance).Name)

			return true
		})

		return &DBNameList{Names: namestrs}, nil

	} else {
		return nil, TICKET_FAIL
	}
	return nil, nil
}

func (s *NetBoltService) Connect(ctx context.Context, in *LoginRequest) (*Ticket, error) {

	if in.Username != "huhong" { //如果集群中的机器则采用myid来认证,集群中的内部调用需要带dbname过来,如果未带则优先采用Session.used，也就是说内部调用一个一连接多库。
		return nil, LOGIN_FAIL
	}
	exist := findSessionByUsername(in.Username)

	if exist == nil {

		token := UUID()
		session := Session{token, in.Db, time.Now().Unix(), in.Username, in.Password}

		sessions.Store(token, &session)

		return &Ticket{Token: token}, nil
	} else {
		return &Ticket{Token: exist.token}, nil
	}

}

func (s *NetBoltService) CreateDB(ctx context.Context, in *CreateDBRequest) (*Response, error) {

	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	if !checkToken(token) {
		return nil, TICKET_FAIL
	}
	_, err = CreateDBAndOpen(in.Dbname)
	if err != nil {
		return &Response{Status: -1, Msg: err.Error()}, err
	} else {
		return &Response{Status: 0, Msg: SUCESS_MSG}, nil
	}
}

func (s *NetBoltService) DropDB(ctx context.Context, in *DropDBRequest) (*Response, error) {

	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	if !checkToken(token) {
		return nil, TICKET_FAIL
	}

	err = DropDB(in.Dbname, in.DeleteFile)

	if err != nil {
		return &Response{Status: -1, Msg: err.Error()}, err
	} else {
		return &Response{Status: 0, Msg: SUCESS_MSG}, nil
	}
}
func (s *NetBoltService) Get(ctx context.Context, in *GetRequest) (*GetResult, error) {
	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	session, ok := getSession(token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}

		value, err := db.Get(in.Key)
		if err != nil {
			return nil, err
		}
		return &GetResult{Key: in.Key, Value: value}, nil
	}
}

func (s *NetBoltService) Put(ctx context.Context, in *PutRequest) (*Response, error) {
	token, err := getIncomingToken(ctx)
	if err != nil {

		fmt.Println("我错误")
		return nil, err
	} else {
		fmt.Println("我错误!")
	}
	session, ok := getSession(token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}

		err := db.Put(in.Key, in.Value)
		if err != nil {
			return &Response{Status: 0, Msg: err.Error()}, err
		} else {
			return &Response{Status: 0, Msg: SUCESS_MSG}, err
		}

	}

}

func (s *NetBoltService) BatchPut(ctx context.Context, in *PutsRequest) (*Response, error) {
	token, err := getIncomingToken(ctx)

	if err != nil {
		return nil, err
	}
	session, ok := getSession(token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}

		err := db.BatchPut(in.Puts)
		if err != nil {
			return &Response{Status: 0, Msg: err.Error()}, err
		} else {
			return &Response{Status: 0, Msg: SUCESS_MSG}, err
		}

	}
}
func (s *NetBoltService) Count(ctx context.Context, in *Ticket) (*CountResult, error) {
	session, ok := getSession(in.Token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}
		return &CountResult{Size: int64(db.Size())}, nil

	}
}

func (s *NetBoltService) Delete(ctx context.Context, in *DeleteRequest) (*Response, error) {

	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	session, ok := getSession(token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}

		err := db.Delete(in.Key)
		if err != nil {
			return &Response{Status: -1, Msg: err.Error()}, err
		} else {
			return &Response{Status: 0}, err
		}
	}
}

func (s *NetBoltService) BatchDelete(ctx context.Context, in *BatchDeleteRequest) (*Response, error) {

	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	session, ok := getSession(token)

	if !ok {
		return nil, TICKET_FAIL
	} else {
		db, ok := GetDatabase(session.used)
		if !ok {
			return nil, DB_NOT_FOUND
		}

		err := db.BatchDelete(in.Keys)
		if err != nil {
			return &Response{Status: -1, Msg: err.Error()}, err
		} else {
			return &Response{Status: 0}, err
		}
	}
}

func (s *NetBoltService) GetCurrentDB(ctx context.Context, in *Ticket) (*DBResponse, error) {
	session, ok := getSession(in.Token)
	if !ok {
		return nil, TICKET_FAIL
	} else {
		return &DBResponse{DbName: session.used}, nil
	}
}

func (s *NetBoltService) Use(ctx context.Context, in *DBRequest) (*DBResponse, error) {

	token, err := getIncomingToken(ctx)
	if err != nil {
		return nil, err
	}
	_, ok := getSession(token)
	if !ok {
		return nil, TICKET_FAIL
	} else {
		_, ok := GetDatabase(in.DbName)
		if !ok {
			return nil, DB_NOT_FOUND
		} else {
			return &DBResponse{DbName: in.DbName}, nil
		}
	}
}

func (s *NetBoltService) Close(ctx context.Context, in *Ticket) (*Response, error) {
	_, ok := getSession(in.Token)
	if !ok {
		return nil, TICKET_FAIL
	} else {
		sessions.Delete(in.Token)
		return &Response{Status: 0}, nil
	}
}

func (s *NetBoltService) Ping(ctx context.Context, in *PingRequest) (*PongResponse, error) {
	return &PongResponse{}, nil
}

//func (s *NetBoltService) CreateCursor(ctx context.Context, in *Ticket) (*Cursor, error) {
//	session, ok := getSession(in.Token)
//	if !ok {
//		return nil, TICKET_FAIL
//	} else {
//		db, ok := GetDatabase(session.used)
//		if !ok {
//			return nil, DB_NOT_FOUND
//		}
//		return db.CreateCursor(in.Token)
//	}
//}
//
//func (s *NetBoltService) Next(ctx context.Context, in *Cursor) (*Entry, error) {
//	session, ok := getSession(in.Token)
//	if !ok {
//		return nil, TICKET_FAIL
//	} else {
//		db, ok := GetDatabase(session.used)
//		if !ok {
//			return nil, DB_NOT_FOUND
//		}
//		return db.Next(in)
//	}
//}

//func (s *NetBoltService) Seek(ctx context.Context, in *Cursor) (*Cursor, error) {
//	return nil, NOT_SUPPROT_ERROR
//}

func (s *NetBoltService) Stream(stream NetBoltService_StreamServer) error {

	token, err := getIncomingToken(stream.Context())
	if err != nil {
		return err
	}
	in, err := stream.Recv()

	if err == io.EOF {
		return nil
	}
	if err != nil {
		return err
	}

	session, ok := getSession(token)
	if !ok {
		return TICKET_FAIL
	}
	db, ok := GetDatabase(session.used)
	if !ok {
		return DB_NOT_FOUND
	}

	return db.Stream(in, func(entry *Entry) (*StreamOp, error) {
		stream.Send(entry)          //发送数据给调用者
		newIn, err := stream.Recv() //从调用者接收下一个命令

		return newIn, err //将下一个调用返回给主函数
	})

}

//func (s *NetBoltService) StreamNext(stream NetBoltService_StreamNextServer) error {
//
//	r,_:=stream.Recv()
//
//	return NOT_SUPPROT_ERROR
//}
